/*
 *      kbd.c
 *
 *      Copyright 2007 Giulio Bai <giulio@hewle.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <config.h>
#include <sys.h>
#include <io2.h>
#include <dev.h>
#include <irq.h>
#include <kbd.h>

const char kbdus[] = {
    0, 0x1b, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=',
    '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
    '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'',
    '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0,
    0, 0, ' '
};


const char kbdus_shift[] = {
    0, 0x1b, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+',
    '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}',
    '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '\"', '~',
    0, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, 0, 0, ' '
};


void kbd_set_leds (int *state)
{
	/* is the keyboard ready? */
	while (inportb (KBD_INDEX_PORT) & 0x02);
	
	outportb (KBD_DATA_PORT, 0xed);
	
	while (inportb (KBD_INDEX_PORT) & 0x02);
	
	outportb (KBD_DATA_PORT, *state);

	// while (inportb (KBD_DATA_PORT) != 0x80);
}


unsigned kbd_special (int scancode)
{
	switch (scancode) {
   		case 0x36:
   	   	case 0x2a:
   	   		/* left or right shift pressed */
         		shift = 1;
         		break;
         		
      		case 0xB6:
      		case 0xaa:
      			/* left or right shift released */
         		shift = 0;
         		break;
         		
      		case 0x1D:
      		 	/* ctrl pressed */
         		ctrl = 1;
         		break;
         		
      		case 0x9D:
      			/* ctrl released */
         		ctrl = 0;
         		break;
         		
      		case 0x38:
      			/* alt pressed */
         		alt = 1;
         		break;
         		
      		case 0xB8:
      			/* alt released */
         		alt = 0;
         		break;
         		
      		case 0x45:
      			/* num lock */
      			if (led & NUM_LOCK) {
				led = (led & ~NUM_LOCK);
			}
			else {
				led = (led | NUM_LOCK);
			}
			kbd_set_leds (&led);
         		break;
         		
		case 0xC5:
			/* num released */
			break;
			
		case 0x46:
			/* scroll lock */
			if (led & SCROLL_LOCK) {
				led = led & ~SCROLL_LOCK;
			}
			else {
				led = led | SCROLL_LOCK;
			}
			kbd_set_leds (&led);
			break;
			
      		case 0xBA:
      			/* caps lock pressed */
      			if (led & CAPS_LOCK) {
				led = led & ~CAPS_LOCK;
			}
			else {
				led = led | CAPS_LOCK;
			}
			kbd_set_leds (&led);
         		//caps = !caps;
         		break;
         		
        	case 0x3A:
      			/* caps released */
      			break;
      			
      		case 0xE0:
         		break;
         		
      		default:
         		return 0;
   	}

   	return 1;
}

int kbd_get_char (int scancode)
{	
	/* can't handle anything > 0x80 */
	if (scancode > 0x80) {
		scancode = 0;
	}
	
	/* lowercase and not shifted chars if caps lock and shift are both 
	 * pressed at the same time */
	if ((shift = (shift ? -4 : 0)) + (led & CAPS_LOCK) == 0) {
		return (kbdus[scancode]);
	}
	/* return uppercase chars otherwise */
	else {
		return (kbdus_shift[scancode]);
	}
}


/* Handles the keyboard interrupt */
void keyboard_handler (struct regs *r)
{
	unsigned char scancode;

	/* Read from the keyboard's data buffer */
	scancode = inportb (KBD_DATA_PORT);
	
	/* Special keys handled separately */
	if (!kbd_special (scancode)) {
		switch (scancode) {
			case 88:
				/* f12 pressed */
				puts ("_f12 pressed_");
				break;
				
			default:
				putch (kbd_get_char (scancode));
				break;
		}
	}
	
	outportb (0x20, 0x20);
	outportb (0xa0, 0x20);
}


/* Loads the keyboard handler */
void keyboard_load ()
{
	/* Installs keyboard_handler to IRQ1 */
	irq_install_handler (1, keyboard_handler);
	
	printi ("keyboard standard keymap", 2, 1);
}
